/**
 * Copyright 1996 self.com.au
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License. (http://www.apache.org/licenses/LICENSE-2.0)
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed 
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for 
 * the specific language governing permissions and limitations under the License.
 *
 * <dhamma-message src="Atisha (11th century Tibetan Buddhist master)">
 * 	The greatest achievement is selflessness. The greatest worth is self-mastery.
 * 	The greatest quality is seeking to serve others. The greatest precept is continual awareness.
 * 	The greatest medicine is the emptiness of everything. The greatest action is not conforming with the worlds ways.
 * 	The greatest magic is transmuting the passions. The greatest generosity is non-attachment.
 * 	The greatest goodness is a peaceful mind. The greatest patience is humility.
 * 	The greatest effort is not concerned with results. The greatest meditation is a mind that lets go.
 * 	The greatest wisdom is seeing through appearances. 
 * </dhamma-message>
 *
 * @author Ashin Wimalajeewa
 *
 * Note, this is a very old runtime library that I have evolved since 1996. I rarely use it
 * now because the Apache Commons project has made it obsolete.
 */
package self.xml;

import java.io.*;
import org.w3c.dom.*;

/*
  This is copied directly from Batik - the SVG header info
*/

public class XmlWriter {
  final static public String VERSION = "<?xml version=\"1.0\"";

//  static public Writer tester;
  static class IndentWriter extends Writer {

    private Writer proxied;
    private int indentLevel;

    public void setIndentLevel(int i) {
      indentLevel = i;
    }

    public int getIndentLevel() {
      return indentLevel;
    }

    public void printIndent() throws IOException {
      proxied.write(XmlWriter.EOL);
      for(int i = indentLevel; i > 0; i--)
        proxied.write(32);

    }

    public Writer getProxied() {
      return proxied;
    }

    public void write(int i) throws IOException {
      proxied.write(i);
    }

    public void write(char ac[]) throws IOException {
      proxied.write(ac);
    }

    public void write(char ac[], int i, int j) throws IOException {
      proxied.write(ac, i, j);
    }

    public void write(String s) throws IOException {
      proxied.write(s);
    }

    public void write(String s, int i, int j) throws IOException {
      proxied.write(s, i, j);
    }

    public void flush() throws IOException {
      proxied.flush();
    }

    public void close() throws IOException {
      proxied.close();
    }

    public IndentWriter(Writer writer) {
      if(writer == null) {
        throw new RuntimeException("proxy should not be null");
      } else {
        proxied = writer;
        return;
      }
    }
  }

  private static String EOL;
  
  //private static final String TAG_END = " />";
  //private static final String TAG_START = "</";
  //private static final String SPACE = " ";

  XmlWriter() {
  	// nothing to do.
  }

  private static void writeXml(Attr attr, IndentWriter indentwriter) throws IOException {
    String s = attr.getName();
    indentwriter.write(s);
    indentwriter.write("=\"");
    writeChildrenXml(attr, indentwriter);
    indentwriter.write(34);
  }

  private static void writeChildrenXml(Attr attr, IndentWriter indentwriter) throws IOException {
    String s = attr.getValue();
    for(int i = 0; i < s.length(); i++) {
      char c = s.charAt(i);
      switch(c) {
      case 60: // '<'
        indentwriter.write("&lt;");
        break;

      case 62: // '>'
        indentwriter.write("&gt;");
        break;

      case 38: // '&'
        indentwriter.write("&amp;");
        break;

      case 39: // '\''
//        indentwriter.write("&apos;"); // asw 07/01/04
        indentwriter.write("&#39;");
        break;

      case 34: // '"'
        indentwriter.write("&quot;");
        break;

      default:
        indentwriter.write(c);
        break;

      }
    }
  }

  private static void writeXml(Comment comment, IndentWriter indentwriter) throws IOException {
    char ac[] = comment.getData().toCharArray();
    indentwriter.write("<!--");
    if(ac != null) {
      boolean flag = false;
      int i = ac.length;
      for(int j = 0; j < i; j++) {
        if(ac[j] == '-')
          if(flag) {
            indentwriter.write(32);
          } else {
            flag = true;
            indentwriter.write(45);
            continue;
          }
        flag = false;
        indentwriter.write(ac[j]);
      }

      if(ac[ac.length - 1] == '-')
        indentwriter.write(32);
    }
    indentwriter.write("-->");
  }

  private static void writeXml(Text text, IndentWriter indentwriter) throws IOException {
//    if ( text.getData().equals("dummy") ) {
//      int i  = 10;
//    }
    char ac[] = text.getData().toCharArray();
    int i = 0;
    int j = 0;
    if(ac == null) {
      System.err.println("Null text data??");
      return;
    }
    for(; j < ac.length; j++) {
      char c = ac[j];
      if(c == '<') {
        indentwriter.write(ac, i, j - i);
        i = j + 1;
        indentwriter.write("&lt;");
      } else
      if(c == '>') {
        indentwriter.write(ac, i, j - i);
        i = j + 1;
        indentwriter.write("&gt;");
      } else
      if(c == '&') {
        indentwriter.write(ac, i, j - i);
        i = j + 1;
        indentwriter.write("&amp;");
      }
    }

    indentwriter.write(ac, i, j - i);

//    if ( tester instanceof StringWriter ) {
//      System.out.println("##############################################");
//      System.out.println( ((StringWriter)tester).toString() );
//      System.out.println("##############################################");
//    }

  }

  private static void writeXml(CDATASection cdatasection, IndentWriter indentwriter) throws IOException {
    char ac[] = cdatasection.getData().toCharArray();
    indentwriter.write("<![CDATA[");
    for(int i = 0; i < ac.length; i++) {
      char c = ac[i];
      if(c == ']' && i + 2 < ac.length && ac[i + 1] == ']' && ac[i + 2] == '>')
        indentwriter.write("]]]]><![CDATA[>");
      else
        indentwriter.write(c);
    }

    indentwriter.write("]]>");
  }

  private static void writeXml(Element element, IndentWriter indentwriter) throws IOException {
    indentwriter.write("</", 0, 1);
    indentwriter.write(element.getTagName());
    NamedNodeMap namednodemap = element.getAttributes();
    if(namednodemap != null) {
      final int i = namednodemap.getLength();
      for (int j = 0; j < i; j++) {
        Attr attr = (Attr)namednodemap.item(j);
        indentwriter.write(32);
        writeXml(attr, indentwriter);
      }
    }
    if(!element.hasChildNodes()) {
      indentwriter.write(" />", 0, 3);
    } else {
      indentwriter.write(" />", 2, 1);
      writeChildrenXml(element, indentwriter);
      indentwriter.write("</", 0, 2);
//      String tagName = element.getTagName();
//      System.out.println("element.getTagName()=" + tagName );
      indentwriter.write(element.getTagName());
      indentwriter.write(" />", 2, 1);
//      if ( tester instanceof StringWriter ) {
//        System.out.println( ((StringWriter)tester).toString() );
//        System.out.println("----------------------------------------------");
//      }
    }
  }

  private static void writeChildrenXml(Element element, IndentWriter indentwriter) throws IOException {
    NodeList nodelist = element.getChildNodes();
    if(nodelist == null)
      return;
    int i = nodelist.getLength();
    int j = 0;
    j = indentwriter.getIndentLevel();
    int lastNodeType = -1;
    try {
      indentwriter.setIndentLevel(j + 2);
      for(int k = 0; k < i; k++) {
        lastNodeType = nodelist.item(k).getNodeType();
        if( lastNodeType != 3) {
          indentwriter.printIndent();
        }
        writeXml(nodelist.item(k), indentwriter);
      }

    }
    finally {
      indentwriter.setIndentLevel(j);
      if ( lastNodeType != 3 )
        indentwriter.printIndent();
//      if ( tester instanceof StringWriter ) {
//        System.out.println( ((StringWriter)tester).toString() );
//        System.out.println("----------------------------------------------");
//      }
    }
  }

  private static void writeDocumentHeader(IndentWriter indentwriter) throws IOException {
    String s = null;
    if(indentwriter.getProxied() instanceof OutputStreamWriter)
      s = java2std(((OutputStreamWriter)indentwriter.getProxied()).getEncoding());
    indentwriter.write( VERSION );
    if(s != null) {
      indentwriter.write(" encoding=\"");
      indentwriter.write(s);
      indentwriter.write(34);
    }
    indentwriter.write("?>");
    indentwriter.write(EOL);
    indentwriter.write(EOL);
//    indentwriter.write("<!DOCTYPE svg PUBLIC '");
//    indentwriter.write("-//W3C//DTD SVG 1.0//EN");
//    indentwriter.write("' '");
//    indentwriter.write("http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd");
//    indentwriter.write("'");
//    indentwriter.write(">");
//    indentwriter.write(EOL);
  }

  private static void writeXml(Document document, IndentWriter indentwriter) throws IOException {
    writeDocumentHeader(indentwriter);
    NodeList nodelist = document.getChildNodes();
    writeXml(nodelist, indentwriter);
  }

  private static void writeXml(NodeList nodelist, IndentWriter indentwriter) throws IOException {
    int i = nodelist.getLength();
    if(i == 0)
      return;
    for(int j = 0; j < i; j++) {
      Node node = nodelist.item(j);
      writeXml(node, indentwriter);
      indentwriter.write(EOL);
    }

  }

  static String java2std(String s) {
    if(s == null)
      return null;
    if(s.startsWith("ISO8859_"))
      return "ISO-8859-" + s.substring(8);
    if(s.startsWith("8859_"))
      return "ISO-8859-" + s.substring(5);
    if("ASCII7".equalsIgnoreCase(s) || "ASCII".equalsIgnoreCase(s))
      return "US-ASCII";
    if("UTF8".equalsIgnoreCase(s))
      return "UTF-8";
    if(s.startsWith("Unicode"))
      return "UTF-16";
    if("SJIS".equalsIgnoreCase(s))
      return "Shift_JIS";
    if("JIS".equalsIgnoreCase(s))
      return "ISO-2022-JP";
    if("EUCJIS".equalsIgnoreCase(s))
      return "EUC-JP";
    else
      return s;
  }

  public static void writeXml(Node node, Writer writer) throws IOException {
    IndentWriter indentwriter = null;
    if(writer instanceof IndentWriter)
      indentwriter = (IndentWriter)writer;
    else
      indentwriter = new IndentWriter(writer);
    switch(node.getNodeType()) {
    case 2: // '\002'
      writeXml((Attr)node, indentwriter);
      break;

    case 8: // '\b'
      writeXml((Comment)node, indentwriter);
      break;

    case 3: // '\003'
      writeXml((Text)node, indentwriter);
      break;

    case 4: // '\004'
      writeXml((CDATASection)node, indentwriter);
      break;

    case 9: // '\t'
      writeXml((Document)node, indentwriter);
      break;

    case 11: // '\013'
      writeDocumentHeader(indentwriter);
      NodeList nodelist = node.getChildNodes();
      writeXml(nodelist, indentwriter);
      break;

    case 1: // '\001'
      writeXml((Element)node, indentwriter);
      break;

    case 5: // '\005'
    case 6: // '\006'
    case 7: // '\007'
    case 10: // '\n'
    default:
      throw new RuntimeException("Unable to write node of type " + node.getClass().getName());

    }
  }

  static  {
    String s;
    try {
      s = System.getProperty("line.separator", "\n");
    }
    catch(SecurityException securityexception) {
      s = "\n";
    }
    EOL = s;
  }
}
